﻿Data Access Objects (DAO)
=========================

ה DAO מספק גישת API כללית למידע המאוחסן בסוגי מסדים שונים (DBMS). כתוצאה מכך, ה DBMS היושב מתחת ניתן לשינוי לאחד אחר ללא צורך בשינוי הקוד המשתמש ב DAO בכדי לגשת למידע.

ה DAO של Yii בנוי על בסיס התוסף של PHP בשם (PDO)](http://php.net/manual/en/book.pdo.php) המספק אפשרויות אחידות לגישה למסדי DBMS נפוצים, כמו MySQL, PostgreSQL. לכן, בכדי להשתמש ב DAO של Yii יש צורך בלהתקין ולהפעיל את התוסף של PDO בשרת, וכמו כן להפעיל את התוספים הספציפים למסד הנתונים בו יהיה שימוש (לדוגמא `PDO_MYSQL`).

ה DAO של Yii בנוי ברובו על בסיס ארבעת המחלקות הבאות:

   - [CDbConnection]: מייצג התחברות למסד נתונים.
   - [CDbCommand]: מייצג שאילתת SQL שצריך להריץ מול מסד נתונים.
   - [CDbDataReader]: מייצג תוצאות שאילתת SQL מול מסד נתונים.
   - [CDbTransaction]: מייצג טרנזקציה.

בחלק זה, אנו נציג כיצד להשתמש ב DAO במקרים שונים.

יצירת התחברות למסד הנתונים
--------------------------------

בכדי ליצר התחברות למסד הנתונים, יש ליצור אובייקט של [CDbConnection] ולהפעיל אותו. יש להגדיר DSN שמהווה בעצם הסטרינג המכיל את המידע הדרוש להתחברות למסד נתונים. כמו כן יהיה צורך בהגדרת שם משתמש וסיסמא להתחברות למסד הנתונים. תזרק שגיאה במידה ותיהיה בעיה בזמן ניסיון החיבור למסד הנתונים (במידה וה DSN לא תקין או שם משתמש וסיסמא לא נכונים).

~~~
[php]
$connection=new CDbConnection($dsn,$username,$password);
// יצירת התחברות רצוי לתחום את זה ב try ... catch
$connection-»active=true;
......
$connection-»active=false;  // סגירת התחברות
~~~

הפורמט של ה DSN תלוי בדרייבר של מסד הנתונים ב PDO שמשתמשים בו. בכללי, DSN מכיל את שם הדרייבר ב PDO, לאחריו נקודותיים ( : ), ולאחריו תחביר ההתחברות המדוייק לכל דרייבר. יש לעיין [דוקומנטציה](http://www.php.net/manual/en/pdo.construct.php) למידע מלא. למטה רשימה של DNS נפוצים:

- SQLite: `sqlite:/path/to/dbfile`
- MySQL: `mysql:host=localhost;dbname=testdb`
- PostgreSQL: `pgsql:host=localhost;port=5432;dbname=testdb`
- SQL Server: `mssql:host=localhost;dbname=testdb`
- Oracle: `oci:dbname=//localhost:1521/testdb`

מאחר ו [CDbConnection] יורשת מהמחלקה [CApplicationComponent], ניתן להשתמש בו גם [כרכיב](/doc/guide/basics.application#application-component). בכדי לעשות זאת, יש להגדיר רכיב [בהגדרות האפליקציה](/doc/guide/basics.application#application-configuration) בשם `db` (או כל שם אחר) בצורה הבאה,

~~~
[php]
array(
    ......
    'components'=»array(
        ......
        'db'=»array(
            'class'=»'CDbConnection',
            'connectionString'=»'mysql:host=localhost;dbname=testdb',
            'username'=»'root',
            'password'=»'password',
            'emulatePrepare'=»true,  // יש צורך בהגדרה זו להתקנות MySQL מסויימות
        ),
    ),
)
~~~

לאחר מכן אנו נוכל לגשת לחיבור ה DB בעזרת `Yii::app()-»db` שכבר הופעלה בצורה אוטומטית. אלה אם כן אנו נגדיר באופן ספציפי את המאפיין [CDbConnection::autoConnect] ל false. שימוש בגישה זו, ההתחברות הזו ניתנת לשימוש במקומות שונים בקוד.

הרצת שאילתות SQL
------------------------

לאחר יצירת התחברות למסד הנתונים, ניתן להריץ שאילתות SQL על ידי שימוש ב [CDbCommand]. ניתן לייצר אובייקט של [CDbCommand] על ידי קריאה ל [()CDbConnection::createCommand] עם השאילתה הבאה:

~~~
[php]
$command=$connection-»createCommand($sql);
// במידה וצריך ניתן לעדכן את שאילתת ה SQL בצורה הבאה:
// $command-»text=$newSQL;
~~~

שאילתת SQL מתבצעת בעזרת [CDbCommand] באחת מהדרכים הבאות:

- [()execute|CDbCommand::execute]: מבצעת שאילתת SQL אשר לא מחזירה מידע לקריאה, כמו `INSERT`, `UPDATE`, `DELETE`. במידה והיא בוצעה בהצלחה היא תחזיר את מספר השורות שעודכנו.

- [()query|CDbCommand::query]: מבצעת שאילתת SQL אשר מחזירה שורות של מידע, כמו `SELECT`. במידה והיא בוצעה בהצלחה, היא מחזירה אובייקט של [CDbDataReader] שבעזרתו ניתן יהיה לרוץ עליו לקבלת המידע שורה שורה. לנוחות, ישנם סט של מתודות `()queryXXX` אשר כלולות שבעזרתם ניתן לקבל את התוצאה ישירות.

תזרק שגיאה במידה והייתה בעיה בהרצת השאילתה.

~~~
[php]
$rowCount=$command-»execute();   // ביצוע שאילתה שלא מחזירה מידע לקריאה
$dataReader=$command-»query();   // הרצת שאילתת SQL
$rows=$command-»queryAll();      // הרצת שאילתה והחזרת כל השורות של התוצאה
$row=$command-»queryRow();       // הרצת שאילתה והחזרת השורה הראשונה בתוצאה
$column=$command-»queryColumn(); // הרצת שאילתה והחזרה העמודה הראשונה של התוצאה
$value=$command-»queryScalar();  // הרצת שאילתה והחזרת העמודה הראשונה בשורה הראשונה
~~~

שליפת תוצאות שאילתה
----------------------

לאחר שהמתודה [()CDbCommand::query] יוצרת את האובייקט של [CDbDataReader], ניתן לקבל את השורות מהמידע שהוחזר על ידי קריאה ל [()CDbDataReader::read] שוב ושוב. ניתן גם להשתמש ב [CDbDataReader] בתוך לולאה `foreach` של PHP בכדי לקבל שורה אחרי שורה.

~~~
[php]
$dataReader=$command-»query();
// קריאה ל read שוב ושוב עד שהוא מחזיר false
while(($row=$dataReader-»read())!==false) { ... }
// שימוש בלולאה על כל התוצאות
foreach($dataReader as $row) { ... }
// קבלת כל התוצאות במכה אחת כמערך
$rows=$dataReader-»readAll();
~~~

» Note|הערה: בניגוד ל [()query|CDbCommand::query], כל המתודות של `queryXXX` מחזירות מידע בצורה ישירה. לדוגמא, [()queryRow|CDbCommand::queryRow] מחזירה מערך המייצג את השורה הראשונה של תוצאת השאילתה.

שימוש בטרנזקציות
------------------

כשאפליקציה מריצה כמה שאילתות, כל אחת קוראת ו/או כותבת מידע למסד הנתונים, חשוב לדעת ולהיות בטוחים שהמסד נתונים מבצע ומריץ את כל השאילתות ולא מפספס אף אחת. טרנזקציה, המיוצגת כאובייקט של [CDbTransaction] ב Yii , ניתנת לשימוש במקרה כזה:

- התחלת הטרנזקציה.
- הרצת השאילתות אחת אחרי השנייה. כל העדכונים למסד לא מוצגים לעולם החיצון.
- ביצוע הטרנזקציה. כעת ניתן לראות את העדכונים במידה והטרנזקציה הסתיימה בהצלחה.
- במידה ואחת מהשאילתות נכשלת בזמן הרצה, כל הטרנזקציה חוזרת אחורה.

רצף העבודה המוצג למעלה ניתן ליישום בעזרת הקוד הבא:

~~~
[php]
$transaction=$connection-»beginTransaction();
try
{
    $connection-»createCommand($sql1)-»execute();
    $connection-»createCommand($sql2)-»execute();
    //.... other SQL executions
    $transaction-»commit();
}
catch(Exception $e) // תתבצע שגיאה במידה וישנה בעיה
{
    $transaction-»rollBack();
}
~~~

תיחום פרמטרים
------------------

בכדי להמנע מהתקפות בעזרת [הזרקות SQL](http://en.wikipedia.org/wiki/SQL_injection) ובכדי לשפר את הביצועים של שאילתות SQL החוזרות על עצמם, ניתן 'להכין' שאילתת SQL עם מקומות שמורים לפרמטרים אשר יתחלפו עם הפרמטרים המקוריים בזמן תהליך תיחום הפרמטרים.

המקומות השמורים לפרמטרים יכולים להיות מוגדרים עם שם יחודי (מאופיינים כמילות מפתח יחודיות) או ללא שם (מאופיינים בעזרת סימני שאלה). יש לקרוא ל [()CDbCommand::bindParam] או [()CDbCommand::bindValue] בכדי להחליף את הפרמטר עם הערך שהוגדר לו. אין צורך לעטוף את הפרמטרים בעזרת מרכאות: מסד הנתונים בו אתה משתמש יעשה זאת אוטומטית. יש לתחום את הפרמטרים לפני קריאה למתודה המבצעת את השאילתה.

~~~
[php]
// שאילתת SQL עם שני מפתחות יחודיים לפרמטרים :username או :email
$sql="INSERT INTO tbl_user (username, email) VALUES(:username,:email)";
$command=$connection-»createCommand($sql);
// החלף את המפתח :username עם ערך שהוגדר לו
$command-»bindParam(":username",$username,PDO::PARAM_STR);
// החלף את המפתח :email עם הערך שהוגדר לו
$command-»bindParam(":email",$email,PDO::PARAM_STR);
$command-»execute();
// הוסף שורה חדשה עם סט חדש של ערכים
$command-»bindParam(":username",$username2,PDO::PARAM_STR);
$command-»bindParam(":email",$email2,PDO::PARAM_STR);
$command-»execute();
~~~

המתודות [()bindParam|CDbCommand::bindParam] ו [()bindValue|CDbCommand::bindValue] הם דומות מאוד. ההבדל היחידי היא שהראשון תוחם פרמטר בעזרת משתנה ב PHP והשני תוחם פרמטר בעזרת ערך. עבור פרמטרים המייצגים בלוקים גדולים של מידע, הראשון הוא עדיף מבחינת ביצועים.

למידע נוסף אודות תיחום פרמטרים, יש לקרוא [בדוקומנטציה של PHP](http://www.php.net/manual/en/pdostatement.bindparam.php).

תיחום עמודות
---------------

בזמן שליפת תוצאות השאילתה, ניתן לתחום עמודות בעזרת משתנים ב PHP כדי שהם יאוכלסו אוטומטית עם הנתונים האחרונים בכל פעם ששורה חדשה נשלפה ממסד הנתונים בעזרת השאילתה, וניתן יהיה לגשת אליהם.

~~~
[php]
$sql="SELECT username, email FROM tbl_user";
$dataReader=$connection-»createCommand($sql)-»query();
// תיחום העמודה הראשונה (username) עם המשתנה $username
$dataReader-»bindColumn(1,$username);
// תיחום העמודה השנייה (email) עם המשתנה $email
$dataReader-»bindColumn(2,$email);
while($dataReader-»read()!==false)
{
    // כעת ניתן להשתמש ב $username ו $email אשר מכילים את המידע מהשורה האחרונה שהגיע ממסד הנתונים.
}
~~~

שימוש בקידומת לטבלאות
------------------

החל מגרסא 1.1.0, Yii מאפשרת תמיכה מובנית לשימוש בקידומת לטבלאות במסד הנתונים. משמעות הקידומת הינה סטרינג אשר מחובר לתחילת שמות הטבלאות במסד בו כרגע משתמשים. בדרך כלל משתמשים באפשרות זו בשרתים שיתופיים אשר מריצים כמה אפליקציות על אותו המסד ומשתמשים בקידומת שונה לטבלאות לכל אפליקציה כדי להבדיל ביניהם. לדוגמא, אפליקציה אחת יכולה להשתמש בקידומת `_tbl` בזמן שאפליקציה נוספת תשתמש בקידומת `_yii`.

בכדי להשתמש בקידומת לטבלאות, יש להגדיר את המאפיין [CDbConnection::tablePrefix] עם הקידומת הרצויה לשימוש. לאחר מכן, בשאילתות ה SQL יש להשתמש ב `{{שם הטבלה}}` בכדי להתייחס לשמות הטבלאות, כש `שם הטבלה` מתייחס לשם הטבלה במסד ללא הקידומת. לדוגמא, אם המסד נתונים מכיל טבלה בשם `tbl_user` כש `_tbl` מוגדר כקידומת לטבלאות, אז נוכל להשתמש בקוד הבא בכדי לשלוף נתונים מהטבלה הזו:

~~~
[php]
$sql='SELECT * FROM {{user}}';
$users=$connection-»createCommand($sql)-»queryAll();
~~~

«div class="revision"»$Id: database.dao.txt 1764 2010-02-01 00:09:12Z qiang.xue $«/div»